import macros, math, strutils, tables, unicode
import types
import helperprocs

export tables

var atoms*: Table[string, JellyLink]

macro atom(atomHeader: untyped, atomBody: untyped): untyped =
  atomHeader.expectKind nnkCall
  atomBody.expectKind nnkStmtList
  let
    atomName = atomHeader[0]
    atomArgs = atomHeader[1..^1]
  case atomArgs.len
  of 0:
    return quote:
      atoms[`atomName`] = toJellyLink(proc(): JellyVal = jelly(`atomBody`))
  of 1:
    var procBody: NimNode
    case atomArgs[0].strVal
    of "any":
      procBody = quote:
        var z {.inject.} = zJelly
        jelly(`atomBody`)
    of "num":
      procBody = quote:
        case zJelly.kind
        of jvInt:
          var z {.inject.} = zJelly.intVal
          return jelly(`atomBody`)
        of jvFloat:
          var z {.inject.} = zJelly.floatVal
          return jelly(`atomBody`)
        of jvStr:
          try:
            var z {.inject.} = zJelly.strVal.initBigInt
            return jelly(`atomBody`)
          except ValueError:
            var z {.inject.} = zJelly.strVal.parseFloat
            return jelly(`atomBody`)
        of jvList:
          var resultS = newSeq[JellyVal](zJelly.list[].len)
          for i in zJelly.list[].low..zJelly.list[].high:
            resultS[i] = atoms[`atomName`](zJelly.list[i])
          return jelly(results)
    of "str":
      procBody = quote:
        var z {.inject.}: string
        case zJelly.kind
        of jvInt:
          z = $zJelly.intVal
        of jvFloat:
          # TODO: proper conversion from float to string
          z = $zJelly.floatVal
        of jvStr:
          z = zJelly.strVal
        of jvList:
          var resultS = newSeq[JellyVal](zJelly.list[].len)
          for i in zJelly.list[].low..zJelly.list[].high:
            resultS[i] = atoms[`atomName`](zJelly.list[i])
          return resultS.jelly
        jelly(`atomBody`)
    else:
      error("Invalid link argument type", atomArgs[0])
    return quote:
      atoms[`atomName`] = toJellyLink(proc(zJelly {.inject.}: JellyVal): JellyVal = `procBody`)
  else:
    error("Link arity can't be higher than 2", atomHeader)

atom "A"(num): z.abs

atom "B"(num): z.toBase(2.initBigInt)

atom "C"(num): 1 - z

atom "D"(num): z.toBase(10.initBigInt)

atom "N"(num): -z

atom "°"(num): z.toFloat * PI / 180.0

atom "¹"(any): z

atom "²"(num): z * z

atom "Ḥ"(num): z + z

atom "W"(any): @[z]

atom "ØB"(): @[0, 1]

atom "Œu"(str): z.toUpper
